# 文件名: app/main.py
import datetime
import json
import uuid

from fastapi import FastAPI, Request, Depends
from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse
from fastapi.middleware.cors import CORSMiddleware
from apscheduler.schedulers.background import BackgroundScheduler
import os
from dotenv import load_dotenv
from sqlalchemy.orm import Session

from app.models.database import create_tables, get_db, SystemSetting, get_real_path, MonitoredUser, Video, \
    get_local_time, SchedulerTask, HotVideo
from app.routes import api, pages, websocket
from app.routes.api import router
from app.services.monitor_service import MonitorService
import sys
from app.utils.logging_config import logger

# 记录启动信息
logger.info("应用启动")


# 获取应用路径
def get_application_path():
    if getattr(sys, 'frozen', False):
        # 打包环境
        return os.path.dirname(sys.executable)
    else:
        # 开发环境
        return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# 设置工作目录
application_path = get_application_path()
os.chdir(application_path)
logger.info(f"应用路径: {application_path}")
logger.info(f"当前工作目录: {os.getcwd()}")

# 加载环境变量
load_dotenv()

# 创建应用
app = FastAPI(title="社交媒体监控", description="监控TikTok、抖音和Instagram用户视频更新")

# 配置CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 挂载静态文件
app.mount("/static", StaticFiles(directory=os.path.join(application_path, "app", "static")), name="static")

# 注册路由
app.include_router(api.router, prefix="/api")
# 注册WebSocket路由
app.include_router(websocket.router)
app.include_router(pages.router)


# 添加首页重定向
@app.get("/")
async def redirect_to_home():
    return RedirectResponse(url="/users")


# 启动定时任务
scheduler = BackgroundScheduler()


# 初始化系统设置
def init_system_settings(db):
    """初始化系统设置（仅在设置不存在时创建）"""

    # 检查设置是否已经存在
    def set_default_if_not_exists(key, value, description):
        existing = SystemSetting.get_value(db, key)
        if existing is None:
            SystemSetting.set_value(db, key, value, description)

    # API令牌
    set_default_if_not_exists(
        "api_token",
        os.getenv("API_TOKEN", ""),
        "API访问令牌，用于调用TikTok/抖音/Instagram API"
    )

    set_default_if_not_exists(
        "tiktok_check_interval",
        os.getenv("TIKTOK_CHECK_INTERVAL", "21600"),  # Default 6 hours
        "TikTok平台检查用户更新的时间间隔（秒）"
    )

    set_default_if_not_exists(
        "douyin_check_interval",
        os.getenv("DOUYIN_CHECK_INTERVAL", "28800"),  # Default 8 hours
        "抖音平台检查用户更新的时间间隔（秒）"
    )

    set_default_if_not_exists(
        "instagram_check_interval",
        os.getenv("INSTAGRAM_CHECK_INTERVAL", "25200"),  # Default 7 hours
        "Instagram平台检查用户更新的时间间隔（秒）"
    )

    set_default_if_not_exists(
        "kuaishou_check_interval",
        os.getenv("INSTAGRAM_CHECK_INTERVAL", "18000"),  # Default 5 hours
        "快手平台检查用户更新的时间间隔（秒）"
    )

    set_default_if_not_exists(
        "tiktok_monitoring_enabled",
        "false",
        "是否启用TikTok平台监控"
    )

    set_default_if_not_exists(
        "douyin_monitoring_enabled",
        "false",
        "是否启用抖音平台监控"
    )

    set_default_if_not_exists(
        "instagram_monitoring_enabled",
        "false",
        "是否启用Instagram平台监控"
    )

    set_default_if_not_exists(
        "kuaishou_monitoring_enabled",
        "false",
        "是否启用快手平台监控"
    )

    # 设置下载重试相关配置
    set_default_if_not_exists(
        "failed_download_retry_interval",
        "14400",  # 4小时 = 14400秒
        "下载失败后的重试间隔（秒）"
    )

    set_default_if_not_exists(
        "max_download_retries",
        "2",
        "下载失败的最大重试次数"
    )

    # 添加重试任务启用设置
    set_default_if_not_exists(
        "retry_task_enabled",
        "false",
        "是否启用下载失败重试任务"
    )

    # 最大重试次数
    set_default_if_not_exists(
        "max_retries",
        "3",
        "下载视频时的最大重试次数"
    )

    # 重试延迟
    set_default_if_not_exists(
        "retry_delays",
        "2,3,4",
        "重试延迟时间（秒），用逗号分隔"
    )

    # 网络请求超时
    set_default_if_not_exists(
        "request_timeout",
        "180",
        "网络请求超时时间（秒）"
    )

    # 随机延迟最小值
    set_default_if_not_exists(
        "min_delay",
        "1.0",
        "随机延迟最小值（秒）"
    )

    # 随机延迟最大值
    set_default_if_not_exists(
        "max_delay",
        "3.0",
        "随机延迟最大值（秒）"
    )

    # TikTok 平台延迟设置
    set_default_if_not_exists(
        "tiktok_user_delay",
        "2",
        "TikTok获取用户信息的延迟时间（秒）"
    )

    set_default_if_not_exists(
        "tiktok_video_delay",
        "3",
        "TikTok获取视频列表的延迟时间（秒）"
    )

    # 抖音平台延迟设置
    set_default_if_not_exists(
        "douyin_user_delay",
        "2",
        "抖音获取用户信息的延迟时间（秒）"
    )

    set_default_if_not_exists(
        "douyin_video_delay",
        "3",
        "抖音获取视频列表的延迟时间（秒）"
    )

    # 下载路径
    set_default_if_not_exists(
        "download_path",
        "",  # 默认为空，表示使用默认路径
        "视频下载保存路径（留空使用默认路径：app/static/downloads）"
    )

    # Instagram平台延迟设置
    set_default_if_not_exists(
        "instagram_user_delay",
        "2",
        "Instagram获取用户信息的延迟时间（秒）"
    )

    set_default_if_not_exists(
        "instagram_video_delay",
        "3",
        "Instagram获取视频列表的延迟时间（秒）"
    )

    # 快手平台延迟设置
    set_default_if_not_exists(
        "kuaishou_user_delay",
        "2",
        "快手获取用户信息的延迟时间（秒）"
    )

    set_default_if_not_exists(
        "kuaishou_video_delay",
        "3",
        "快手获取视频列表的延迟时间（秒）"
    )

    # 添加热门视频相关设置
    set_default_if_not_exists(
        "hot_videos_enabled",
        "false",
        "是否启用热门视频功能"
    )

    set_default_if_not_exists(
        "hot_videos_check_interval",
        "86400",  # 默认每天一次
        "热门视频检查更新间隔（秒）"
    )

    set_default_if_not_exists(
        "hot_videos_daily_hour",
        "6",  # 默认每天早上6点
        "每天拉取热门视频的时间点（小时，0-23）"
    )

    set_default_if_not_exists(
        "hot_videos_pages",
        "2",  # 默认拉取2页
        "每次拉取热门视频的页数"
    )

    # 热门视频保留天数
    set_default_if_not_exists(
        "hot_videos_retention_days",
        "7",  # 默认保留7天
        "热门视频保留天数（过期且的未下载的视频将被删除）"
    )

    # 热门视频最大数量
    set_default_if_not_exists(
        "hot_videos_max_count",
        "1000",  # 默认最多保留1000个
        "热门视频最大保留数量（超出时会删除最旧的且未下载的视频）"
    )

    # 添加视频屏幕方向设置
    set_default_if_not_exists(
        "download_video_orientation",
        "both",  # 默认同时下载横屏和竖屏视频，可选值: "both", "portrait", "landscape"
        "视频下载方向设置（both: 全部下载, portrait: 仅竖屏, landscape: 仅横屏）"
    )

    # 添加热门视频相关设置
    set_default_if_not_exists(
        "use_date_enabled",
        "false",
        "是否建立日期文件夹"
    )


# 初始化任务记录
def init_scheduler_tasks(db):
    """初始化任务记录"""
    task_ids = [
        ("check_tiktok_updates", "TikTok检查更新"),
        ("check_douyin_updates", "抖音检查更新"),
        ("check_instagram_updates", "Instagram检查更新"),
        ("check_kuaishou_updates", "快手检查更新"),
        ("retry_failed_downloads", "下载失败重试"),
        ("fetch_hot_videos", "拉取热门视频")
    ]

    for task_id, task_name in task_ids:
        task = db.query(SchedulerTask).filter(SchedulerTask.task_id == task_id).first()
        if not task:
            task = SchedulerTask(
                task_id=task_id,
                task_name=task_name,
                status="idle"
            )
            db.add(task)

    db.commit()
    logger.info("任务记录初始化完成")


def update_next_run_time(db, task_id):
    """更新任务的下次运行时间"""
    try:
        job = scheduler.get_job(task_id)
        if not job:
            logger.warning(f"任务 {task_id} 在调度器中不存在")
            return False

        task = db.query(SchedulerTask).filter(SchedulerTask.task_id == task_id).first()
        if not task:
            logger.warning(f"任务 {task_id} 在数据库中不存在")
            return False

        # 获取下次运行时间
        next_run_time = None

        # 方法1: 尝试使用触发器的get_next_fire_time方法
        if hasattr(job.trigger, 'get_next_fire_time'):
            next_run_time = job.trigger.get_next_fire_time(None, get_local_time())

        # 方法2: 如果方法1失败，尝试其他方式获取
        if not next_run_time and hasattr(job, '_get_run_times'):
            try:
                run_times = job._get_run_times(get_local_time())
                if run_times and len(run_times) > 0:
                    next_run_time = run_times[0]
            except:
                pass

        # 更新数据库中的下次运行时间
        if next_run_time:
            task.next_run_at = next_run_time
            db.commit()
            logger.info(f"已更新任务 {task_id} 的下次运行时间: {next_run_time}")
            return True
        else:
            logger.warning(f"无法获取任务 {task_id} 的下次运行时间")
            return False
    except Exception as e:
        logger.error(f"更新任务 {task_id} 的下次运行时间失败: {str(e)}")
        return False


# 添加定时任务的配置函数
def add_check_task(db):
    # Get platform-specific check intervals
    try:
        tiktok_interval = int(SystemSetting.get_value(db, "tiktok_check_interval", "21600")) + 0
        douyin_interval = int(SystemSetting.get_value(db, "douyin_check_interval", "28800")) + 300
        instagram_interval = int(SystemSetting.get_value(db, "instagram_check_interval", "25200")) + 600
        kuaishou_interval = int(SystemSetting.get_value(db, "kuaishou_check_interval", "18000")) + 900

        # Get platform enabled status
        tiktok_enabled = SystemSetting.get_value(db, "tiktok_monitoring_enabled", "true").lower() == "true"
        douyin_enabled = SystemSetting.get_value(db, "douyin_monitoring_enabled", "true").lower() == "true"
        instagram_enabled = SystemSetting.get_value(db, "instagram_monitoring_enabled", "true").lower() == "true"
        kuaishou_enabled = SystemSetting.get_value(db, "kuaishou_monitoring_enabled", "true").lower() == "true"
    except:
        tiktok_interval = 21600 + 0  # Default 3 hour
        douyin_interval = 28800 + 300  # Default 4 hours
        instagram_interval = 25200 + 600  # Default 5 hours
        kuaishou_interval = 18000 + 900  # Default 4 hours
        tiktok_enabled = True
        douyin_enabled = True
        instagram_enabled = True
        kuaishou_enabled = True

    # Configure TikTok check job
    job = scheduler.get_job('check_tiktok_updates')
    if tiktok_enabled:
        if job:
            scheduler.reschedule_job(
                'check_tiktok_updates',
                trigger='interval',
                seconds=tiktok_interval
            )
        else:
            scheduler.add_job(
                check_platform_updates,
                'interval',
                seconds=tiktok_interval,
                id='check_tiktok_updates',
                args=['tiktok']
            )
    else:
        # 如果禁用了监控，删除任务
        if job:
            scheduler.remove_job('check_tiktok_updates')
            logger.info("已禁用TikTok监控任务")

    # Configure Douyin check job
    job = scheduler.get_job('check_douyin_updates')
    if douyin_enabled:
        if job:
            scheduler.reschedule_job(
                'check_douyin_updates',
                trigger='interval',
                seconds=douyin_interval
            )
        else:
            scheduler.add_job(
                check_platform_updates,
                'interval',
                seconds=douyin_interval,
                id='check_douyin_updates',
                args=['douyin']
            )
    else:
        # 如果禁用了监控，删除任务
        if job:
            scheduler.remove_job('check_douyin_updates')
            logger.info("已禁用抖音监控任务")

    # Configure Instagram check job
    job = scheduler.get_job('check_instagram_updates')
    if instagram_enabled:
        if job:
            scheduler.reschedule_job(
                'check_instagram_updates',
                trigger='interval',
                seconds=instagram_interval
            )
        else:
            scheduler.add_job(
                check_platform_updates,
                'interval',
                seconds=instagram_interval,
                id='check_instagram_updates',
                args=['instagram']
            )
    else:
        # 如果禁用了监控，删除任务
        if job:
            scheduler.remove_job('check_instagram_updates')
            logger.info("已禁用Instagram监控任务")

    # Configure 快手 check job
    job = scheduler.get_job('check_kuaishou_updates')
    if tiktok_enabled:
        if job:
            scheduler.reschedule_job(
                'check_kuaishou_updates',
                trigger='interval',
                seconds=kuaishou_interval
            )
        else:
            scheduler.add_job(
                check_platform_updates,
                'interval',
                seconds=kuaishou_interval,
                id='check_kuaishou_updates',
                args=['kuaishou']
            )
    else:
        # 如果禁用了监控，删除任务
        if job:
            scheduler.remove_job('check_kuaishou_updates')
            logger.info("已禁用快手监控任务")

    # 打印启用状态
    logger.info(f"平台监控状态: TikTok={tiktok_enabled}, 抖音={douyin_enabled}, Instagram={instagram_enabled}, 快手={kuaishou_enabled}")

    # 打印定时任务配置
    enabled_tasks = []
    if tiktok_enabled:
        enabled_tasks.append(f"TikTok每 {tiktok_interval} 秒")
    if douyin_enabled:
        enabled_tasks.append(f"抖音每 {douyin_interval} 秒")
    if instagram_enabled:
        enabled_tasks.append(f"Instagram每 {instagram_interval} 秒")
    if kuaishou_enabled:
        enabled_tasks.append(f"快手每 {kuaishou_interval} 秒")

    if enabled_tasks:
        logger.info(f"已启用的定时任务：" + ", ".join(enabled_tasks))
    else:
        logger.info("所有平台监控任务均已禁用")

    # 更新任务的下次运行时间
    for task_id in ['check_tiktok_updates', 'check_douyin_updates', 'check_instagram_updates', 'check_kuaishou_updates']:
        job = scheduler.get_job(task_id)
        if job:
            db_task = db.query(SchedulerTask).filter(SchedulerTask.task_id == task_id).first()
            if db_task:
                try:
                    # 方法1: 尝试使用 _get_run_times 方法
                    next_times = job._get_run_times(get_local_time())
                    if next_times and len(next_times) > 0:
                        db_task.next_run_at = next_times[0]
                except:
                    # 方法2: 尝试从触发器直接获取
                    if hasattr(job, 'trigger') and hasattr(job.trigger, 'get_next_fire_time'):
                        next_fire_time = job.trigger.get_next_fire_time(None, get_local_time())
                        if next_fire_time:
                            db_task.next_run_at = next_fire_time
                db.commit()


# 按平台检查用户更新
def check_platform_updates(platform):
    try:
        # 获取数据库会话
        db = next(get_db())

        # 更新任务状态为运行中
        task_id = f"check_{platform}_updates"
        SchedulerTask.update_task_status(db, task_id, "running")

        # 初始化服务时传入数据库会话
        monitor_service = MonitorService(db)

        # 确保API令牌正确设置
        api_token = SystemSetting.get_value(db, "api_token",
                                            os.getenv("API_TOKEN",
                                                      ""))
        logger.info(f"{platform}定时任务使用API令牌: {api_token[:5]}...")
        monitor_service.api_service.update_token(api_token)

        # 获取该平台的所有用户
        users = db.query(MonitoredUser).filter(MonitoredUser.platform == platform).all()

        if not users:
            logger.warning(f"未找到需要检查的{platform}用户")
            # 完成后更新任务状态
            SchedulerTask.update_task_status(db, task_id, "completed", f"成功检查 {len(users)} 个用户")

            # 获取最新的下次运行时间
            update_next_run_time(db, task_id)

            db.close()
            return

        logger.info(f"开始检查{platform}平台的{len(users)}个用户...")

        import asyncio
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        # 运行平台特定的检查更新任务
        try:
            for user in users:
                if platform == "tiktok":
                    loop.run_until_complete(monitor_service.check_tiktok_updates(db, user))
                elif platform == "douyin":
                    loop.run_until_complete(monitor_service.check_douyin_updates(db, user))
                elif platform == "instagram":
                    loop.run_until_complete(monitor_service.check_instagram_updates(db, user))
                elif platform == "kuaishou":
                    loop.run_until_complete(monitor_service.check_kuaishou_updates(db, user))
        except Exception as e:
            logger.error(f"检查{platform}更新过程中发生错误: {str(e)}")
        finally:
            loop.close()

        # 完成后更新任务状态
        SchedulerTask.update_task_status(db, task_id, "completed", f"成功检查 {len(users)} 个用户")

        # 获取最新的下次运行时间
        update_next_run_time(db, task_id)

        # 关闭数据库会话
        db.close()

        logger.info(f"{platform}平台定时检查更新完成: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    except Exception as e:
        # 出错时更新任务状态
        try:
            db = next(get_db())
            SchedulerTask.update_task_status(db, f"check_{platform}_updates", "failed", f"错误: {str(e)}")
            db.close()
        except:
            pass

        logger.error(f"{platform}平台定时任务执行失败: {str(e)}")


def format_number(value):
    """格式化数字，将大数转为带单位的形式
    例如：1000 -> 1K，1000000 -> 1M
    """
    value = int(value)
    if value < 1000:
        return str(value)
    elif value < 1000000:
        return f"{value / 1000:.1f}K".replace('.0K', 'K')
    else:
        return f"{value / 1000000:.1f}M".replace('.0M', 'M')


# 定义启动事件
@app.on_event("startup")
async def startup_event():
    # 创建数据库表（仅在不存在时创建）
    try:
        create_tables()
        logger.info("数据库检查完成")
    except Exception as e:
        logger.error(f"创建数据库表时出错: {str(e)}")

    # 获取数据库会话
    db = next(get_db())

    # 初始化任务记录
    try:
        init_scheduler_tasks(db)
    except Exception as e:
        logger.error(f"初始化任务记录时出错: {str(e)}")

    # 初始化系统设置（仅设置不存在的配置）
    try:
        init_system_settings(db)
        # 打印API令牌进行验证
        api_token = SystemSetting.get_value(db, "api_token", "默认令牌")
        logger.info(f"启动时API令牌: {api_token[:5]}...")
    except Exception as e:
        logger.error(f"初始化系统设置时出错: {str(e)}")

    # 创建下载目录
    download_dir = os.path.join(get_real_path(), "app", "static", "downloads")
    os.makedirs(download_dir, exist_ok=True)
    logger.info(f"下载目录已创建: {download_dir}")

    # 启动定时任务
    global scheduler
    scheduler = BackgroundScheduler()

    # 添加定时任务
    add_check_task(db)

    # 关闭数据库会话
    db.close()

    # 启动调度器
    scheduler.start()
    logger.info("定时任务调度器已启动")

    # 添加下载重试任务
    add_retry_task(db)

    # 添加热门视频定时任务
    add_hot_videos_task(db)

    # 添加Jinja2过滤器
    from app.routes.pages import templates
    templates.env.filters["format_number"] = format_number


# 定时重试下载失败的视频
def retry_failed_downloads():
    """定时重试下载失败的视频"""
    try:
        # 获取数据库会话
        db = next(get_db())

        # 更新任务状态为运行中
        SchedulerTask.update_task_status(db, "retry_failed_downloads", "running")

        # 获取当前时间
        now = get_local_time()

        # 获取配置项
        max_retries = int(SystemSetting.get_value(db, "max_download_retries", "2"))
        retry_interval = int(SystemSetting.get_value(db, "failed_download_retry_interval", "14400"))

        logger.info(f"开始重试下载失败的视频，最大重试次数: {max_retries}, 重试间隔: {retry_interval}秒")

        # 查询需要重试的视频
        # 条件: 1. 状态为failed 2. 重试次数小于最大重试次数 2. 下次重试时间已到或未设置
        retry_videos = db.query(Video).filter(
            Video.download_status == "failed",
            Video.retry_count < max_retries,
            (Video.next_retry_at <= now) | (Video.next_retry_at == None)
        ).all()

        if not retry_videos:
            logger.info("没有需要重试的视频")
            # 更新任务状态为运行中
            SchedulerTask.update_task_status(db, "retry_failed_downloads", "completed", "没有需要重试的视频")

            # 更新下次运行时间
            update_next_run_time(db, "retry_failed_downloads")

            db.close()
            return

        logger.info(f"找到 {len(retry_videos)} 个需要重试下载的视频")

        # 初始化服务
        monitor_service = MonitorService(db)

        # 确保API令牌正确设置
        api_token = SystemSetting.get_value(db, "api_token",
                                            os.getenv("API_TOKEN",
                                                      ""))
        monitor_service.api_service.update_token(api_token)

        # 创建异步循环
        import asyncio
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        # 逐个重试下载
        for video in retry_videos:
            try:
                # 获取视频信息
                video_id = video.video_id
                platform = video.platform

                # 获取用户信息
                user = db.query(MonitoredUser).filter(MonitoredUser.id == video.user_id).first()
                if not user:
                    logger.warning(f"视频ID {video.id} 的用户不存在，跳过重试")
                    continue

                username = user.username

                # 获取下载URL
                try:
                    download_urls = json.loads(video.download_url)
                    if not download_urls:
                        logger.warning(f"视频 {video_id} 没有下载URL，跳过重试")
                        continue
                except Exception as e:
                    logger.error(f"解析视频 {video_id} 的下载URL失败: {str(e)}")
                    continue

                # 更新重试计数
                video.retry_count += 1

                logger.info(
                    f"重试下载视频: ID={video_id}, 平台={platform}, 用户={username}, 重试次数={video.retry_count + 1}")

                # 更新状态为正在下载
                video.download_status = "downloading"
                db.commit()

                # 执行下载
                success = loop.run_until_complete(
                    monitor_service.download_video(db, video, download_urls, user.username, user.sequence_number, user.category)
                )

                if success:
                    logger.info(f"视频 {video.id} 重试下载成功")
                else:
                    logger.warning(f"视频 {video_id} 第 {video.retry_count} 次重试下载失败")
                    # 如果达到最大重试次数
                    if video.retry_count >= max_retries:
                        logger.warning(f"视频 {video_id} 已达到最大重试次数 {max_retries}，不再重试")
            except Exception as e:
                logger.error(f"重试下载视频 {video.id} 时出错: {str(e)}", exc_info=True)

        loop.close()

        # 完成后更新任务状态
        retry_count = len(retry_videos) if retry_videos else 0
        SchedulerTask.update_task_status(db, "retry_failed_downloads", "completed", f"重试了 {retry_count} 个视频")

        # 更新下次运行时间
        update_next_run_time(db, "retry_failed_downloads")

        db.close()
        logger.info("重试下载任务完成")

    except Exception as e:
        # 出错时更新任务状态
        try:
            db = next(get_db())
            SchedulerTask.update_task_status(db, "retry_failed_downloads", "failed", f"错误: {str(e)}")
            db.close()
        except:
            pass

        logger.error(f"重试下载任务执行失败: {str(e)}", exc_info=True)


# 将重试任务添加到调度器中
def add_retry_task(db):
    # 从数据库获取重试间隔，如果不存在则使用默认值
    try:
        retry_interval = int(SystemSetting.get_value(db, "failed_download_retry_interval", "14400")) + 100
        retry_enabled = SystemSetting.get_value(db, "retry_task_enabled", "true").lower() == "true"

        logger.info(f"配置下载重试任务: 启用={retry_enabled}, 间隔={retry_interval}秒")

        # 获取当前时间
        now = get_local_time()

        # 计算首次运行时间（应用启动后5分钟）
        next_run = now + datetime.timedelta(minutes=5)

        # 获取现有任务
        job = scheduler.get_job('retry_failed_downloads')

        if retry_enabled:
            # 如果任务已存在，重新调度
            if job:
                # 先移除旧任务，然后添加新任务，避免调度冲突
                scheduler.remove_job('retry_failed_downloads')
                logger.info("已移除旧的下载重试任务")

            # 添加新任务
            scheduler.add_job(
                func=retry_failed_downloads,
                trigger='interval',
                seconds=retry_interval,
                id='retry_failed_downloads',
                next_run_time=next_run
            )
            logger.info(f"已添加下载重试任务，下次运行时间: {next_run}")

            # 验证任务是否添加成功
            job = scheduler.get_job('retry_failed_downloads')
            if job:
                # 手动更新数据库中的任务状态
                db_task = db.query(SchedulerTask).filter(SchedulerTask.task_id == 'retry_failed_downloads').first()
                if db_task:
                    db_task.next_run_at = next_run
                    db.commit()
                    logger.info(f"已手动更新数据库任务状态，下次运行时间: {next_run}")
                logger.info(f"下载重试任务已配置，每 {retry_interval} 秒（{retry_interval / 3600:.1f} 小时）检查一次")
            else:
                logger.error("添加任务后无法获取任务")
        else:
            # 如果禁用了任务，删除现有任务
            if job:
                scheduler.remove_job('retry_failed_downloads')
                logger.info("已禁用下载重试任务")

    except Exception as e:
        logger.error(f"配置下载重试任务时出错: {str(e)}", exc_info=True)


# 定义热门视频任务配置函数
def add_hot_videos_task(db):
    try:
        # 获取热门视频设置
        enabled = SystemSetting.get_value(db, "hot_videos_enabled", "true").lower() == "true"
        check_interval = int(SystemSetting.get_value(db, "hot_videos_check_interval", "86400"))
        daily_hour = int(SystemSetting.get_value(db, "hot_videos_daily_hour", "6"))

        logger.info(f"配置热门视频任务: 启用={enabled}, 间隔={check_interval}秒, 每日时间={daily_hour}时")

        # 获取当前时间
        now = get_local_time()

        # 计算下一次定期运行时间 - 今天的指定小时
        next_regular_run = now.replace(hour=daily_hour, minute=0, second=0, microsecond=0)

        # 如果当前时间已经过了今天的运行时间，设置为明天
        if now > next_regular_run:
            next_regular_run = next_regular_run + datetime.timedelta(days=1)

        # 添加或重新配置任务
        job = scheduler.get_job('fetch_hot_videos')

        if enabled:
            # 配置定期运行任务
            if job:
                # 先移除旧任务，然后添加新任务，避免调度冲突
                scheduler.remove_job('fetch_hot_videos')

            # 添加定期运行任务
            scheduler.add_job(
                func=fetch_hot_videos_task,
                trigger='interval',
                seconds=check_interval,
                id='fetch_hot_videos',
                next_run_time=next_regular_run
            )
            logger.info(f"已添加定期运行任务，下次运行时间: {next_regular_run}")

            # 验证定期任务是否添加成功
            regular_job = scheduler.get_job('fetch_hot_videos')
            if regular_job:
                # 手动更新数据库中的任务状态
                db_task = db.query(SchedulerTask).filter(SchedulerTask.task_id == 'fetch_hot_videos').first()
                if db_task:
                    db_task.next_run_at = next_regular_run
                    db.commit()
                    logger.info(f"已手动更新数据库任务状态，下次运行时间: {next_regular_run}")
            else:
                logger.error("添加定期任务后无法获取任务")
        else:
            # 如果禁用了任务，删除所有相关任务
            if job:
                scheduler.remove_job('fetch_hot_videos')

            # 检查并删除首次运行任务
            immediate_job = scheduler.get_job('fetch_hot_videos_first_run')
            if immediate_job:
                scheduler.remove_job('fetch_hot_videos_first_run')

            logger.info("已禁用热门视频任务")
    except Exception as e:
        logger.error(f"配置热门视频任务时出错: {str(e)}", exc_info=True)


# 定义热门视频拉取任务
def fetch_hot_videos_task():
    """热门视频定期任务（更新下次运行时间）"""
    try:
        # 获取数据库会话
        db = next(get_db())

        logger.info("执行热门视频定期任务")

        # 更新任务状态为运行中
        SchedulerTask.update_task_status(db, "fetch_hot_videos", "running", "定期运行")

        # 获取配置项
        pages = int(SystemSetting.get_value(db, "hot_videos_pages", "2"))

        # 获取保留天数设置（默认7天）
        retention_days = int(SystemSetting.get_value(db, "hot_videos_retention_days", "7"))

        # 获取保留的最大视频数（默认1000个）
        max_videos = int(SystemSetting.get_value(db, "hot_videos_max_count", "1000"))

        logger.info(f"开始拉取热门视频，计划拉取 {pages} 页，保留 {retention_days} 天内或最多 {max_videos} 个视频")

        # 初始化服务
        monitor_service = MonitorService(db)

        # 确保API令牌正确设置
        api_token = SystemSetting.get_value(db, "api_token",
                                            os.getenv("API_TOKEN",
                                                      ""))
        monitor_service.api_service.update_token(api_token)

        # 创建异步循环
        import asyncio
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        # 逐页拉取热门视频
        total_saved = 0
        for page in range(1, pages + 1):
            try:
                result = loop.run_until_complete(
                    monitor_service.fetch_douyin_hot_videos(db, page, 20, 24, None)
                )
                if result:
                    total_saved += result.get("saved_count", 0)
            except Exception as e:
                logger.error(f"拉取热门视频第 {page} 页时出错: {str(e)}")

        loop.close()

        # 执行数据清理
        try:
            # 1. 按时间清理 - 删除超过保留天数的视频
            retention_date = get_local_time() - datetime.timedelta(days=retention_days)
            # 先查询要删除的记录数，便于记录日志
            old_videos_count = db.query(HotVideo).filter(
                HotVideo.created_at < retention_date,
                HotVideo.is_favorite == False,  # 不删除收藏的视频
                HotVideo.download_status != 'downloaded'  # 不删除已下载的视频
            ).count()

            # 执行删除操作
            if old_videos_count > 0:
                logger.info(f"清理 {retention_days} 天前的热门视频: {old_videos_count} 个")
                db.query(HotVideo).filter(
                    HotVideo.created_at < retention_date,
                    HotVideo.is_favorite == False,  # 不删除收藏的视频
                    HotVideo.download_status != 'downloaded'  # 不删除已下载的视频
                ).delete(synchronize_session=False)
                db.commit()

            # 2. 按数量清理 - 如果总数超过最大值，删除多余的视频（保留最新的和已收藏/已下载的）
            total_videos = db.query(HotVideo).count()
            if total_videos > max_videos:
                # 计算需要删除的数量
                delete_count = total_videos - max_videos
                logger.info(f"热门视频总数 {total_videos} 超过最大限制 {max_videos}，将清理 {delete_count} 个")

                # 查找需要保留的视频ID
                protected_ids = db.query(HotVideo.id).filter(
                    (HotVideo.is_favorite == True) |
                    (HotVideo.download_status == 'downloaded')
                ).all()
                protected_ids = [id[0] for id in protected_ids]

                # 查找可删除的最旧视频
                deletable_videos = db.query(HotVideo).filter(
                    ~HotVideo.id.in_(protected_ids)
                ).order_by(HotVideo.created_at.asc()).limit(delete_count).all()

                delete_ids = [video.id for video in deletable_videos]
                if delete_ids:
                    db.query(HotVideo).filter(HotVideo.id.in_(delete_ids)).delete(synchronize_session=False)
                    db.commit()
                    logger.info(f"已清理 {len(delete_ids)} 个最旧的热门视频")

            clean_stats = {
                "old_videos_removed": old_videos_count,
                "excess_videos_removed": len(delete_ids) if 'delete_ids' in locals() else 0,
                "total_after_cleanup": db.query(HotVideo).count()
            }
            logger.info(f"数据清理完成: {clean_stats}")

        except Exception as e:
            logger.error(f"清理热门视频数据时出错: {str(e)}")
            db.rollback()

        # 完成后更新任务状态
        SchedulerTask.update_task_status(db, "fetch_hot_videos", "completed",
                                         f"定期运行成功，保存 {total_saved} 个热门视频")

        # 更新下次运行时间
        update_next_run_time(db, "fetch_hot_videos")

        db.close()
        logger.info(f"热门视频定期任务完成，共保存 {total_saved} 个视频")

    except Exception as e:
        # 出错时更新任务状态
        try:
            db = next(get_db())
            SchedulerTask.update_task_status(db, "fetch_hot_videos", "failed", f"定期运行错误: {str(e)}")
            db.close()
        except:
            pass

        logger.error(f"热门视频定期任务执行失败: {str(e)}", exc_info=True)

